package com.wzh.util;

/**
 * @Author zh wang
 * @Create 2025/6/16 15:55
 */
import com.wzh.exception.CurrencyMismatchException;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Currency;
import java.util.Objects;

public final class Money implements Comparable<Money> {
    private final long amount;
    private final Currency currency;

    private Money(long amount, Currency currency) {
        this.amount = amount;
        this.currency = Objects.requireNonNull(currency, "Currency must not be null");
    }

    public static Money of(long minorAmount, Currency currency) {
        return new Money(minorAmount, currency);
    }

    public static Money ofMajor(BigDecimal majorAmount, Currency currency) {
        BigDecimal scaled = majorAmount.scaleByPowerOfTen(currency.getDefaultFractionDigits());
        return new Money(scaled.longValueExact(), currency);
    }

    // 算术运算方法
    public Money add(Money other) {
        checkCurrencyMatch(other);
        return new Money(Math.addExact(amount, other.amount), currency);
    }

    public Money subtract(Money other) {
        checkCurrencyMatch(other);
        return new Money(Math.subtractExact(amount, other.amount), currency);
    }

    public Money multiply(long multiplier) {
        return new Money(Math.multiplyExact(amount, multiplier), currency);
    }

    public Money divide(long divisor, RoundingMode roundingMode) {
        BigDecimal result = BigDecimal.valueOf(amount)
                .divide(BigDecimal.valueOf(divisor), roundingMode);
        return new Money(result.longValueExact(), currency);
    }

    /**
     * 格式化方法
     * @return
     */
    public BigDecimal toMajorUnits() {
        return BigDecimal.valueOf(amount)
                .movePointLeft(currency.getDefaultFractionDigits());
    }

    public String toDisplayString() {
        return String.format("%s %,.2f",
                currency.getCurrencyCode(),
                toMajorUnits());
    }

    // 数据库辅助方法
    public String getCurrencyCode() {
        return currency.getCurrencyCode();
    }

    public long getStorageAmount() {
        return amount;
    }

    // 重写equals/hashCode/compareTo
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Money)) return false;
        Money money = (Money) o;
        return amount == money.amount &&
                currency.equals(money.currency);
    }

    @Override
    public int hashCode() {
        return 31 * Long.hashCode(amount) + currency.hashCode();
    }

    @Override
    public int compareTo(Money other) {
        checkCurrencyMatch(other);
        return Long.compare(amount, other.amount);
    }

    private void checkCurrencyMatch(Money other) {
        if (!currency.equals(other.currency)) {
            throw new CurrencyMismatchException(
                    String.format("Currency mismatch: %s != %s",
                            currency, other.currency));
        }
    }
}
